<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>统计图表节点</title>
    <style>
      #mountNode {
        background: #001528;
      }
      .graph-tooltip {
        position: absolute;
        top: 0;
        left: 0;
        border: 1px solid #e2e2e2;
        border-radius: 4px;
        font-size: 12px;
        color: #545454;
        background-color: rgba(255, 255, 255, 0.9);
        padding: 10px 8px;
        box-shadow: rgb(174, 174, 174) 0px 0px 10px;
      }
    </style>
  </head>
  <body>
    <div id="mountNode"></div>
    <script src="../build/g6.js"></script>
    <script>
      /**
       *  该案例演示如何使用G6自定义面积图节点
       *  by 镜曦
       *
       */
      /**
       * 注册一个类似南丁格尔玫瑰一样的节点
       */
      G6.registerNode('circleBar', {
        draw(cfg, group) {
          const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
          const width = size[0];
          const height = size[1];
          /*
          G：
          Fan
          x: 扇形圆心的 x 坐标
          y: 扇形圆心的 y 坐标
          rs: 内圈半径
          re: 外圈半径
          startAngle: 起点弧度
          endAngle: 终点弧度
          clockwise: 为true时顺时针渲染，为false时逆时针渲染
        */
          const baseR = 30;
          let nowAngle = 0;
          const everyIncAngle = (2 * Math.PI * (360 / 5 / 5)) / 360;
          cfg.details.forEach(cat => {
            cat.values.forEach(item => {
              const re = item + baseR;
              const fan = group.addShape('fan', {
                attrs: {
                  x: 0,
                  y: 0,
                  rs: baseR,
                  re: item + baseR,
                  startAngle: nowAngle,
                  endAngle: (nowAngle += everyIncAngle),
                  clockwise: false,
                  stroke: 'darkgray',
                  fill: cat.color,
                },
              });
              // 加上交互动画
              fan.on('mouseenter', function(evt) {
                fan.animate(
                  {
                    re: re + 8,
                  },
                  {
                    repeat: false,
                    duration: 300,
                  },
                );
              });
              fan.on('mouseleave', function(evt) {
                fan.animate(
                  {
                    re: re,
                  },
                  {
                    repeat: false,
                    duration: 300,
                  },
                );
              });

              // 设置class
              fan.set('className', 'littleCircle');
            });
          });
          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: baseR,
              fill: cfg.centerColor,
              stroke: 'darkgray',
            },
          });
          if (cfg.label) {
            group.addShape('text', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                textAlign: 'center',
                textBaseline: 'middle',
                text: cfg.label,
                fill: 'white',
                fontStyle: 'bold',
              },
            });
          }
          return group;
        },
      });

      /**
       * 注册一个 分布在圆周上的折线图
       */
      G6.registerNode('circleLine', {
        draw(cfg, group) {
          const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
          const width = size[0];
          const height = size[1];

          const baseR = 30;
          let nowAngle = 0;

          // Ref line
          let refR = baseR;
          const refInc = 10;
          for (let i = 0; i < 5; i++) {
            group.addShape('circle', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                r: (refR += refInc),
                stroke: 'rgba(255,255,255,0.4)',
                lineDash: [4, 4],
              },
            });
          }

          const everyIncAngle = (2 * Math.PI * (360 / 5 / 5)) / 360;
          cfg.details.forEach(cat => {
            // 计算一系列点的位置
            const postions = [];
            cat.values.forEach((item, index) => {
              const r = baseR + item;
              const xPos = r * Math.cos(nowAngle);
              const yPos = r * Math.sin(nowAngle);
              nowAngle += everyIncAngle;
              postions.push([xPos, yPos]);
              if (index === 4) {
                const r = baseR + item;
                const xPos = r * Math.cos(nowAngle);
                const yPos = r * Math.sin(nowAngle);
                postions.push([xPos, yPos]);
              }
            });
            const pathArrayL = postions.map(item => ['L', ...item]);
            // 添加连线
            const shape = group.addShape('path', {
              attrs: {
                path: [
                  ['M', 0, 0], // 上部顶点
                  ...pathArrayL,
                  ['Z'], // 封闭
                ],
                stroke: cat.color, // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
              },
            });
            // 添加标注点
            postions.forEach((pos, index) => {
              if (index !== 5) {
                const littleCircle = group.addShape('circle', {
                  // attrs: style
                  attrs: {
                    x: pos[0], // 居中
                    y: pos[1],
                    r: 2,
                    fill: 'black',
                    stroke: cat.color,
                    cursor: 'pointer',
                  },
                });
                // 加上交互动画
                littleCircle.on('mouseenter', function(evt) {
                  littleCircle.animate(
                    {
                      r: 5,
                    },
                    {
                      repeat: false,
                      duration: 200,
                    },
                  );
                });
                littleCircle.on('mouseleave', function(evt) {
                  littleCircle.animate(
                    {
                      r: 2,
                    },
                    {
                      repeat: false,
                      duration: 200,
                    },
                  );
                });
                // 设置class
                littleCircle.set('className', 'littleCircle');
              }
            });

            /*
          const shape = group.addShape('path', {
            attrs: {
              path: [
                ['M', 0, 0 ], // 上部顶点
                ['L', width / 2, 0], // 右侧点
                ['L', 0, height / 2], // 下部
                ['L', - width / 2, 0], // 左侧
                ['Z'] // 封闭
              ],
              stroke: cfg.color // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
            }
          });
          
          */
          });

          // 添加一个和背景色相同的圆形
          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: baseR,
              fill: cfg.centerColor,
              stroke: 'darkgray',
            },
          });
          if (cfg.label) {
            group.addShape('text', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                textAlign: 'center',
                textBaseline: 'middle',
                text: cfg.label,
                fill: 'white',
                fontStyle: 'bold',
              },
            });
          }
          return group;
        },
      });

      /**
       * 注册一个 只有标注点
       */
      G6.registerNode('justPoints', {
        draw(cfg, group) {
          const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
          const width = size[0];
          const height = size[1];

          const baseR = 30;
          let nowAngle = 0;

          // Ref line
          let refR = baseR;
          const refInc = 10;
          for (let i = 0; i < 5; i++) {
            group.addShape('circle', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                r: (refR += refInc),
                stroke: 'rgba(255,255,255,0.4)',
                lineDash: [4, 4],
              },
            });
          }
          const everyIncAngle = (2 * Math.PI * (360 / 5 / 5)) / 360;
          nowAngle = nowAngle + everyIncAngle / 2;
          cfg.details.forEach(cat => {
            // 计算一系列点的位置
            const postions = [];
            cat.values.forEach((item, index) => {
              const r = baseR + item;
              const xPos = r * Math.cos(nowAngle);
              const yPos = r * Math.sin(nowAngle);
              nowAngle += everyIncAngle;
              postions.push([xPos, yPos]);
              if (index === 4) {
                const r = baseR + item;
                const xPos = r * Math.cos(nowAngle);
                const yPos = r * Math.sin(nowAngle);
                postions.push([xPos, yPos]);
              }
            });

            // 添加标注点
            postions.forEach((pos, index) => {
              if (index !== 5) {
                group.addShape('circle', {
                  // attrs: style
                  attrs: {
                    x: pos[0], // 居中
                    y: pos[1],
                    r: 2,
                    fill: 'black',
                    stroke: cat.color,
                  },
                });
              }
            });

            /*
          const shape = group.addShape('path', {
            attrs: {
              path: [
                ['M', 0, 0 ], // 上部顶点
                ['L', width / 2, 0], // 右侧点
                ['L', 0, height / 2], // 下部
                ['L', - width / 2, 0], // 左侧
                ['Z'] // 封闭
              ],
              stroke: cfg.color // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
            }
          });
          
          */
          });

          let nowAngle2 = 0;
          const everyIncAngleCat = (2 * Math.PI * (360 / 5)) / 360;
          for (let i = 0; i < 5; i++) {
            const r = 30 + 50;
            const xPos = r * Math.cos(nowAngle2);
            const yPos = r * Math.sin(nowAngle2);

            const shape = group.addShape('path', {
              attrs: {
                path: [
                  ['M', 0, 0],
                  ['L', xPos, yPos],
                ],
                lineDash: [4, 4],

                stroke: 'darkgray', // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
              },
            });
            nowAngle2 += everyIncAngleCat;
          }
          // 添加一个和背景色相同的圆形
          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: baseR,
              fill: cfg.centerColor,
              stroke: 'darkgray',
            },
          });

          if (cfg.label) {
            group.addShape('text', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                textAlign: 'center',
                textBaseline: 'middle',
                text: cfg.label,
                fill: 'white',
                fontStyle: 'bold',
              },
            });
          }
          return group;
        },
      });

      /**
       * 注册一个 面积图节点
       */
      G6.registerNode('area', {
        draw(cfg, group) {
          const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
          const width = size[0];
          const height = size[1];

          const baseR = 30;
          let nowAngle = 0;

          // Ref line
          let refR = baseR;
          const refInc = 10;
          for (let i = 0; i < 6; i++) {
            group.addShape('circle', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                r: (refR += refInc),
                stroke: 'rgba(255,255,255,0.4)',
                lineDash: [4, 4],
              },
            });
          }
          const everyIncAngle = (2 * Math.PI * (360 / 5)) / 360;
          const tempIncValues = [baseR, baseR, baseR, baseR, baseR];
          const allRs = [];
          cfg.details.forEach(cat => {
            const oneRs = [];
            cat.values.forEach((v, i) => {
              const R = tempIncValues[i] + v * 0.4;
              oneRs.push(R);
              tempIncValues[i] = R;
            });
            allRs.push(oneRs);
          });
          const strokeColors = [
            'rgba(37,203,253,1)',
            'rgba(254,255,123,1)',
            'rgba(254,171,58,1)',
            'rgba(254,87,102,1)',
            'rgba(22,193,118,1)',
          ];
          const fillColors = [
            'rgba(37,203,253,0.5)',
            'rgba(254,255,123,0.5)',
            'rgba(254,171,58,0.5)',
            'rgba(254,87,102,0.5)',
            'rgba(22,193,118,0.5)',
          ];

          allRs.reverse().forEach((Rs, index) => {
            let curAngle = 0;
            const poss = [];
            Rs.forEach(r => {
              const xPos = r * Math.cos(curAngle);
              const yPos = r * Math.sin(curAngle);
              curAngle += everyIncAngle;
              poss.push([xPos, yPos]);
            });
            const Ls = poss.map((p, i) => {
              if (i === 0) {
                return ['M', ...p];
              }
              return ['L', ...p];
            });
            console.log('Ls', ...Ls);
            const shape = group.addShape('path', {
              attrs: {
                path: [
                  ...Ls,
                  ['Z'], // 封闭
                ],
                stroke: strokeColors[index],
                fill: fillColors[index],
              },
            });
          });
          let nowAngle2 = 0;
          const everyIncAngleCat = (2 * Math.PI * (360 / 5)) / 360;
          for (let i = 0; i < 5; i++) {
            const r = 30 + 60;
            const xPos = r * Math.cos(nowAngle2);
            const yPos = r * Math.sin(nowAngle2);

            const shape = group.addShape('path', {
              attrs: {
                path: [
                  ['M', 0, 0],
                  ['L', xPos, yPos],
                ],
                lineDash: [4, 4],

                stroke: 'darkgray', // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
              },
            });
            nowAngle2 += everyIncAngleCat;
          }

          // 添加一个和背景色相同的圆形
          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: baseR,
              fill: cfg.centerColor,
              stroke: 'darkgray',
            },
          });

          if (cfg.label) {
            group.addShape('text', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                textAlign: 'center',
                textBaseline: 'middle',
                text: cfg.label,
                fill: 'white',
                fontStyle: 'bold',
              },
            });
          }
          return group;
        },
      });

      /**
      环 1
    */
      G6.registerNode('rings1', {
        draw(cfg, group) {
          const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
          const width = size[0];
          const height = size[1];
          /*
          G：
          Fan
          x: 扇形圆心的 x 坐标
          y: 扇形圆心的 y 坐标
          rs: 内圈半径
          re: 外圈半径
          startAngle: 起点弧度
          endAngle: 终点弧度
          clockwise: 为true时顺时针渲染，为false时逆时针渲染
        */
          const baseR = 30;
          let nowAngle = 0;
          const everyIncAngle = (2 * Math.PI * (360 / 5 / 5)) / 360;
          cfg.details.forEach(cat => {
            cat.values.forEach(item => {
              const baseNbr = Math.ceil(item / 10);
              const baseIncR = 7;
              let nowStartR = baseR;
              const last = item % 10;
              const endAngle = nowAngle + everyIncAngle;
              for (let i = 0; i < baseNbr; i++) {
                const fan = group.addShape('fan', {
                  attrs: {
                    x: 0,
                    y: 0,
                    rs: nowStartR,
                    re: nowStartR + baseIncR,
                    startAngle: nowAngle,
                    endAngle: endAngle,
                    clockwise: false,
                    stroke: 'darkgray',
                    fill: cat.color,
                  },
                });
                nowStartR = nowStartR + baseIncR + 2;
                if (i === baseNbr - 1 && last !== 0) {
                  const fan = group.addShape('fan', {
                    attrs: {
                      x: 0,
                      y: 0,
                      rs: nowStartR,
                      re: nowStartR + (baseIncR * last) / 10,
                      startAngle: nowAngle,
                      endAngle: endAngle,
                      clockwise: false,
                      stroke: 'darkgray',
                      fill: cat.color,
                    },
                  });
                }
              }
              nowAngle = endAngle;
            });
          });

          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: baseR,
              fill: cfg.centerColor,
              stroke: 'darkgray',
            },
          });
          if (cfg.label) {
            group.addShape('text', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                textAlign: 'center',
                textBaseline: 'middle',
                text: cfg.label,
                fill: 'white',
                fontStyle: 'bold',
              },
            });
          }
          return group;
        },
      });

      /**
       * 注册一个 面积图节点
       */
      G6.registerNode('rings2', {
        draw(cfg, group) {
          const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
          const width = size[0];
          const height = size[1];

          const baseR = 30;
          let nowAngle = 0;

          // Ref line
          let refR = baseR;
          const refInc = 10;
          for (let i = 0; i < 6; i++) {
            group.addShape('circle', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                r: (refR += refInc),
                stroke: 'rgba(255,255,255,0.4)',
                lineDash: [4, 4],
              },
            });
          }
          const everyIncAngle = (2 * Math.PI * (360 / 5)) / 360;
          const tempIncValues = [baseR, baseR, baseR, baseR, baseR];
          const allRs = [];
          cfg.details.forEach(cat => {
            const oneRs = [];
            cat.values.forEach((v, i) => {
              const R = tempIncValues[i] + v * 0.4;
              oneRs.push(R);
              tempIncValues[i] = R;
            });
            allRs.push(oneRs);
          });
          const strokeColors = [
            'rgba(37,203,253,1)',
            'rgba(254,255,123,1)',
            'rgba(254,171,58,1)',
            'rgba(254,87,102,1)',
            'rgba(22,193,118,1)',
          ];
          const fillColors = [
            'rgba(37,203,253,0.5)',
            'rgba(254,255,123,0.5)',
            'rgba(254,171,58,0.5)',
            'rgba(254,87,102,0.5)',
            'rgba(22,193,118,0.5)',
          ];

          allRs.reverse().forEach((Rs, index) => {
            let curAngle = 0;
            const poss = [];
            Rs.forEach(r => {
              const baseNbr = Math.ceil(r / 10);
              const baseIncR = 7;
              let nowStartR = baseR;
              const last = r % 10;

              for (let i = 0; i < baseNbr; i++) {
                const endAngle = nowAngle + everyIncAngle;

                const fan = group.addShape('fan', {
                  attrs: {
                    x: 0,
                    y: 0,
                    rs: nowStartR,
                    re: nowStartR + baseIncR,
                    startAngle: nowAngle,
                    endAngle: endAngle,
                    clockwise: false,
                    stroke: 'darkgray',
                    fill: strokeColors[index],
                  },
                });
                nowStartR = nowStartR + baseIncR + 2;
                if (i === baseNbr - 1 && last !== 0) {
                  const fan = group.addShape('fan', {
                    attrs: {
                      x: 0,
                      y: 0,
                      rs: nowStartR,
                      re: nowStartR + (baseIncR * last) / 10,
                      startAngle: nowAngle,
                      endAngle: endAngle,
                      clockwise: false,
                      stroke: 'darkgray',
                      fill: strokeColors[index],
                    },
                  });
                }
                nowAngle = endAngle;
              }
            });
          });

          // 添加一个和背景色相同的圆形
          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: baseR,
              fill: cfg.centerColor,
              stroke: 'darkgray',
            },
          });

          if (cfg.label) {
            group.addShape('text', {
              // attrs: style
              attrs: {
                x: 0, // 居中
                y: 0,
                textAlign: 'center',
                textBaseline: 'middle',
                text: cfg.label,
                fill: 'white',
                fontStyle: 'bold',
              },
            });
          }
          return group;
        },
      });

      /** 数据 */
      const data = {
        nodes: [
          {
            id: 'nodeA',
            x: 150,
            y: 150,
            label: 'Bar',
            shape: 'circleBar',
            anchorPoints: [
              [0, 0.5],
              [1, 0.5],
            ],
            details: [
              { cat: 'pv', values: [20, 30, 40, 30, 30], color: '#25cbfd' },
              { cat: 'dal', values: [40, 30, 20, 30, 50], color: '#feff7b' },
              { cat: 'uv', values: [40, 30, 30, 40, 40], color: '#feab3a' },
              { cat: 'sal', values: [20, 30, 50, 20, 20], color: '#fe5766' },
              { cat: 'cal', values: [10, 10, 20, 20, 20], color: '#16c176' },
            ],
            centerColor: '#0066FF',
          },
          {
            id: 'nodeB',
            x: 400,
            y: 150,
            label: 'Line',
            shape: 'circleLine',
            anchorPoints: [
              [0, 0.5],
              [1, 0.5],
            ],
            details: [
              { cat: 'pv', values: [20, 30, 40, 30, 30], color: '#25cbfd' },
              { cat: 'dal', values: [40, 30, 20, 30, 50], color: '#feff7b' },
              { cat: 'uv', values: [40, 30, 30, 40, 40], color: '#feab3a' },
              { cat: 'sal', values: [20, 30, 50, 20, 20], color: '#fe5766' },
              { cat: 'cal', values: [10, 10, 20, 20, 20], color: '#16c176' },
            ],
            centerColor: '#0066FF',
          },

          {
            id: 'nodeC',
            x: 650,
            y: 150,
            label: 'Point',
            shape: 'justPoints',
            anchorPoints: [
              [0, 0.5],
              [1, 0.5],
            ],
            details: [
              { cat: 'pv', values: [20, 30, 40, 30, 30], color: '#25cbfd' },
              { cat: 'dal', values: [40, 30, 20, 30, 50], color: '#feff7b' },
              { cat: 'uv', values: [40, 30, 30, 40, 40], color: '#feab3a' },
              { cat: 'sal', values: [20, 30, 50, 20, 20], color: '#fe5766' },
              { cat: 'cal', values: [10, 10, 20, 20, 20], color: '#16c176' },
            ],
            centerColor: '#0066FF',
          },

          {
            id: 'nodeD',
            x: 150,
            y: 400,
            label: 'Area',
            shape: 'area',
            anchorPoints: [
              [0, 0.5],
              [1, 0.5],
            ],
            details: [
              { cat: 'pv', values: [20, 30, 40, 30, 30], color: '#25cbfd' },
              { cat: 'dal', values: [40, 30, 20, 30, 50], color: '#feff7b' },
              { cat: 'uv', values: [40, 30, 30, 40, 40], color: '#feab3a' },
              { cat: 'sal', values: [20, 30, 50, 20, 20], color: '#fe5766' },
              { cat: 'cal', values: [10, 10, 20, 20, 20], color: '#16c176' },
            ],
            centerColor: '#0066FF',
          },

          {
            id: 'nodeF',
            x: 400,
            y: 400,
            label: 'Rings1',
            shape: 'rings1',
            anchorPoints: [
              [0, 0.5],
              [1, 0.5],
            ],
            details: [
              { cat: 'pv', values: [20, 30, 48, 30, 30], color: '#25cbfd' },
              { cat: 'dal', values: [40, 30, 20, 30, 50], color: '#feff7b' },
              { cat: 'uv', values: [40, 30, 30, 4, 40], color: '#feab3a' },
              { cat: 'sal', values: [20, 30, 50, 20, 20], color: '#fe5766' },
              { cat: 'cal', values: [10, 10, 25, 20, 20], color: '#16c176' },
            ],
            centerColor: '#0066FF',
          },
        ],
        edges: [
          //{source:'nodeA', target:'nodeB', color:"rgba(0, 255, 255, 0.5)"},
          //{source:'nodeB', target:'nodeC',  color:"rgba(0, 255, 255, 0.5)"},
        ],
      };

      const graph = new G6.Graph({
        container: 'mountNode',
        width: 1000,
        height: 600,
      });

      graph.on('node:mouseenter', function(event) {
        var node = event.item;
        var nodeId = node.get('model').id;
        var shape = event.target;

        if (shape.get('className') === 'littleCircle') {
          // 如果点击是发生在节点里面的小圆上，显示tooltip
          console.log('x', event);
          console.log('Y', event);

          showTooltip('tooltip for ' + nodeId, {
            x: event.x,
            y: event.y,
          });
        } else {
          // 否则隐藏tooltip
          hideTooltip();
        }
      });
      graph.on('node:mouseleave', function(event) {
        hideTooltip();
      });

      graph.data(data);
      graph.render();

      var tooltipEl = null;
      // 在指定的位置显示tooltip
      function showTooltip(message, position) {
        const offSetX = 50;
        if (!tooltipEl) {
          var container = document.getElementById('mountNode');
          tooltipEl = document.createElement('div');
          tooltipEl.setAttribute('class', 'graph-tooltip');
          container.appendChild(tooltipEl);
        }
        tooltipEl.textContent = message;
        // tooltip是相对于画布canvas element绝对定位，所以position的x，y必须是相对于画布的坐标
        tooltipEl.style.left = position.x + offSetX + 'px';
        tooltipEl.style.top = position.y + 'px';
        tooltipEl.style.display = 'block';
      }

      // 隐藏tooltip
      function hideTooltip() {
        if (!tooltipEl) {
          return;
        }
        tooltipEl.style.display = 'none';
      }
    </script>
  </body>
</html>
